package com.freewater.cargotracker.application.internal;

import com.freewater.cargotracker.application.BookingService;
import com.freewater.cargotracker.domain.model.cargo.*;
import com.freewater.cargotracker.domain.model.location.Location;
import com.freewater.cargotracker.domain.model.location.LocationRepository;
import com.freewater.cargotracker.domain.model.location.UnLocode;
import com.freewater.cargotracker.domain.service.AllocationCheckerService;
import com.freewater.cargotracker.domain.service.OverBookingPolicy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.List;

public class DefaultBookingService implements BookingService {

    private final OverBookingPolicy overBookingPolicy;
    private final CargoRepository cargoRepository;
    private final LocationRepository locationRepository;
    private final AllocationCheckerService allocationCheckerService;
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    public DefaultBookingService(OverBookingPolicy overBookingPolicy,
                                 CargoRepository cargoRepository,
                                 LocationRepository locationRepository,
                                 AllocationCheckerService allocationCheckerService) {
        this.overBookingPolicy = overBookingPolicy;
        this.cargoRepository = cargoRepository;
        this.locationRepository = locationRepository;
        this.allocationCheckerService = allocationCheckerService;
    }

    @Override
    public TrackingId bookNewCargo(UnLocode origin, UnLocode destination, LocalDateTime arrivalDeadline) {
        final TrackingId trackingId = this.cargoRepository.nextTrackingId();
        final Location originLocation = this.locationRepository.findByCode(origin);
        final Location destinationLocation = this.locationRepository.findByCode(destination);
        RouteSpecification specification = new RouteSpecification(originLocation, destinationLocation, arrivalDeadline);
        // 1、构建Cargo
        Cargo cargo = new Cargo(trackingId, specification);
        // 2、判断是否可以还可以预定
        EnterpriseSegment enterpriseSegment = this.allocationCheckerService.derivedEnterpriseSegment(cargo);
        // 2.1、已经预定的数量
        int quantityBooked = this.cargoRepository.quantityBooked(enterpriseSegment);
        // 2.2、是否可以再预定
        boolean accept = this.allocationCheckerService.mayAccept(cargo, quantityBooked);
        if (!accept) {
            throw new IllegalArgumentException("已经超量，不能再预定");
        }
        // 3、持久化货物运输预定
        this.cargoRepository.save(cargo);
        logger.info("Booked new cargo with trackingId {}", cargo.getTrackingId().getIdString());
        return cargo.getTrackingId();
    }

    @Override
    public List<Itinerary> requestPossibleRoutesForCargo(TrackingId trackingID) {
        return null;
    }

    @Override
    public void assignCargoToRoute(TrackingId trackingId, Itinerary itinerary) {
        final Cargo cargo = this.cargoRepository.findByTrackingId(trackingId);
        cargo.assignToRoute(itinerary);
        logger.info("Cargo was assigned to route {}", itinerary);
        this.cargoRepository.save(cargo);
    }
}
